; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes=dse < %s | FileCheck %s

target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128"

declare void @llvm.lifetime.start.p0(i64, ptr nocapture) nounwind
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) nounwind
declare void @llvm.memset.p0.i8(ptr nocapture, i8, i8, i1) nounwind

define void @test1() {
; CHECK-LABEL: @test1(
; CHECK-NEXT:    [[A:%.*]] = alloca i8, align 1
; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 1, ptr [[A]])
; CHECK-NEXT:    ret void
;
  %A = alloca i8

  store i8 0, ptr %A  ;; Written to by memset
  call void @llvm.lifetime.end.p0(i64 1, ptr %A)

  call void @llvm.memset.p0.i8(ptr %A, i8 0, i8 -1, i1 false)

  ret void
}

define void @test2(ptr %P) {
; CHECK-LABEL: @test2(
; CHECK-NEXT:    [[Q:%.*]] = getelementptr i32, ptr [[P:%.*]], i32 1
; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 4, ptr [[Q]])
; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 4, ptr [[Q]])
; CHECK-NEXT:    ret void
;
  %Q = getelementptr i32, ptr %P, i32 1
  call void @llvm.lifetime.start.p0(i64 4, ptr %Q)
  store i32 0, ptr %Q  ;; This store is dead.
  call void @llvm.lifetime.end.p0(i64 4, ptr %Q)
  ret void
}

; lifetime.end only marks the first two bytes of %A as dead. Make sure
; `store i8 20, ptr %A.2 is not removed.
define void @test3_lifetime_end_partial() {
; CHECK-LABEL: @test3_lifetime_end_partial(
; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 2, ptr [[A]])
; CHECK-NEXT:    [[A_1:%.*]] = getelementptr i8, ptr [[A]], i64 1
; CHECK-NEXT:    [[A_2:%.*]] = getelementptr i8, ptr [[A]], i64 2
; CHECK-NEXT:    store i8 20, ptr [[A_2]], align 1
; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 2, ptr [[A]])
; CHECK-NEXT:    call void @use(ptr [[A_1]])
; CHECK-NEXT:    ret void
;
  %A = alloca i32

  call void @llvm.lifetime.start.p0(i64 2, ptr %A)
  %A.1 = getelementptr i8, ptr %A, i64 1
  %A.2 = getelementptr i8, ptr %A, i64 2

  store i8 0, ptr %A
  store i8 10, ptr %A.1
  store i8 20, ptr %A.2

  call void @llvm.lifetime.end.p0(i64 2, ptr %A)
  call void @use(ptr %A.1)
  ret void
}

; lifetime.end only marks the first two bytes of %A as dead. Make sure
; `store i8 20, ptr %A.2 is not removed.
define void @test4_lifetime_end_partial_loop() {
; CHECK-LABEL: @test4_lifetime_end_partial_loop(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 2, ptr [[A]])
; CHECK-NEXT:    [[A_1:%.*]] = getelementptr i8, ptr [[A]], i64 1
; CHECK-NEXT:    [[A_2:%.*]] = getelementptr i8, ptr [[A]], i64 2
; CHECK-NEXT:    call void @use(ptr [[A_1]])
; CHECK-NEXT:    store i8 20, ptr [[A_2]], align 1
; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 2, ptr [[A]])
; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 10
; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i8 [[IV_NEXT]], 10
; CHECK-NEXT:    br i1 [[EXITCOND]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %A = alloca i32

  br label %loop

loop:
  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ]
  call void @llvm.lifetime.start.p0(i64 2, ptr %A)
  %A.1 = getelementptr i8, ptr %A, i64 1
  %A.2 = getelementptr i8, ptr %A, i64 2

  call void @use(ptr %A.1)

  store i8 20, ptr %A.2
  store i8 10, ptr %A.1
  store i8 0, ptr %A
  call void @llvm.lifetime.end.p0(i64 2, ptr %A)

  %iv.next = add i8 %iv, 10
  %exitcond = icmp eq i8 %iv.next, 10
  br i1 %exitcond, label %exit, label %loop

exit:
  ret void
}

; lifetime.end only marks the first two bytes of %A as dead. Make sure
; `store i8 20, ptr %A.2 is not removed.
define void @test5_lifetime_end_partial(ptr %A) {
; CHECK-LABEL: @test5_lifetime_end_partial(
; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 2, ptr [[A:%.*]])
; CHECK-NEXT:    [[A_1:%.*]] = getelementptr i8, ptr [[A]], i64 1
; CHECK-NEXT:    [[A_2:%.*]] = getelementptr i8, ptr [[A]], i64 2
; CHECK-NEXT:    store i8 20, ptr [[A_2]], align 1
; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 2, ptr [[A]])
; CHECK-NEXT:    call void @use(ptr [[A_1]])
; CHECK-NEXT:    store i8 30, ptr [[A_1]], align 1
; CHECK-NEXT:    store i8 40, ptr [[A_2]], align 1
; CHECK-NEXT:    ret void
;

  call void @llvm.lifetime.start.p0(i64 2, ptr %A)
  %A.1 = getelementptr i8, ptr %A, i64 1
  %A.2 = getelementptr i8, ptr %A, i64 2

  store i8 0, ptr %A
  store i8 10, ptr %A.1
  store i8 20, ptr %A.2

  call void @llvm.lifetime.end.p0(i64 2, ptr %A)

  call void @use(ptr %A.1)
  store i8 30, ptr %A.1
  store i8 40, ptr %A.2
  ret void
}

declare void @use(ptr) readonly
